﻿using Encloser.Packed;
using System;

namespace Encloser
{
    public class Result<T> : Result
    {
        protected readonly T data;

        public readonly static new Result<T> OK = new Result<T>(true, default, null, null);
        public readonly static new Result<T> FAIL = new Result<T>(false, default, null, null);
        public readonly static new Result<T> INVALID_PARAMETERS = new Result<T>(false, default, "参数不合法", null);

        protected Result(bool success, T data = default, string message = null, Exception exception = null) : base(success, message, exception)
        {
            this.data = data;
        }

        public Result<T> Try(Action<T> action)
        {
            if (success)
            {
                try
                {
                    action?.Invoke(data);
                }
                catch (Exception e)
                {
                    return Failed(e);
                }
            }
            return this;
        }

        public Result<V> Try<V>(Func<T, V> func)
        {
            if (success)
            {
                if (func == null) return Result<V>.INVALID_PARAMETERS;
                try
                {
                    return Result<V>.Success(func.Invoke(data));
                }
                catch (Exception e)
                {
                    return Result<V>.Failed(e);
                }
            }
            return Result<V>.Failed(message, exception);
        }

        public Result<V> Try<V>(Func<T, Result<V>> func)
        {
            if (success)
            {
                if (func == null) return Result<V>.INVALID_PARAMETERS;
                try
                {
                    return func.Invoke(data);
                }
                catch (Exception e)
                {
                    return Result<V>.Failed(e);
                }
            }
            return Result<V>.Failed(message, exception);
        }

        public static Result<T> Success(T data)
        {
            return new Result<T>(true, data);
        }

        public new static Result<T> Failed(Exception exception = null)
        {
            return new Result<T>(false, default, exception?.Message, exception);
        }

        public new static Result<T> Failed(string message, Exception exception = null)
        {
            return new Result<T>(false, default, message ?? exception?.Message, exception);
        }
    }
}
